package com.dawn.framework.apisign.core.aspects;

import com.alibaba.fastjson.JSONObject;
import com.dawn.framework.apisign.core.annotation.Sign;
import com.dawn.framework.apisign.core.exception.SignException;
import com.dawn.framework.web.core.util.RequestUtils;
import com.dawn.framework.web.core.util.WebFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @description:
 * @author: cnsu-cmh
 * @time: 2024/8/7 14:59
 */
@Slf4j
@Aspect
public class SignAspect {

    @Around("@annotation(signAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint, Sign signAnnotation) throws Throwable {
        String signKey = signAnnotation.signKey();
        int timeout = signAnnotation.timeout();
        String timeStampKey = signAnnotation.timeStampKey();
        String secretKey = signAnnotation.secretKey();

        HttpServletRequest request = WebFrameworkUtils.getRequest();
        String sign = request.getHeader(signKey);
        String timeStamp = request.getHeader(timeStampKey);
        String secret = request.getHeader(secretKey);

        if (StringUtils.isEmpty(sign)
                || StringUtils.isEmpty(timeStamp)
                || StringUtils.isEmpty(secret)
        ) {
            if (!signAnnotation.hideError()) {
                throw new SignException("验签失败，缺少必要header");
            } else {
                return null;
            }
        }
        if ((System.currentTimeMillis() - Long.parseLong(timeStamp)) / 1000 > timeout) {
            throw new SignException("请求已过期");
        }
        // 所有参数和时间戳一起逆序相加(使用+号连接)，并做md5
//        Object[] requestArgs = joinPoint.getArgs();
//        List<Object> signArgs = new ArrayList<>();
        JSONObject reqParamJSON = RequestUtils.getReqParamJSON(request);
        Map<String, Object> signArgs = new LinkedHashMap<>();
        reqParamJSON.entrySet().stream()
                .sorted(Map.Entry.<String, Object>comparingByKey().reversed())
                .forEachOrdered(e -> signArgs.put(e.getKey(), e.getValue()));
//        for (Map.Entry<String, Object> entry : reqParamJSON.entrySet()) {
//            signArgs.add(entry.getValue());
//        }
        StringBuffer mergeStrBuffer = new StringBuffer();
        signArgs.forEach((k,v) -> {
            mergeStrBuffer.append(k + ":" + v + "+");
        });
        String mergeStr = mergeStrBuffer
                .append("sc=").append(secret)
                .append("&t=").append(timeStamp).toString();
        String signForCompare = DigestUtils.md5DigestAsHex(mergeStr.getBytes(StandardCharsets.UTF_8)).toUpperCase();
        if (!signForCompare.equals(sign)) {
            // 签名比对失败
            if (!signAnnotation.hideError()) {
                throw new SignException("验签失败，参数:" + mergeStr + ",服务端签名:" + signForCompare);
            } else {
                log.warn("可能有人伪造接口，验签失败，参数:" + mergeStr + ",服务端签名:" + signForCompare);
                return null;
            }
        }
        return joinPoint.proceed();
    }



}
